/*
 * @Author: 本章节主要是基础数据类型使用范例
 * @Date: 2021-01-18 13:25:31
 * @LastEditTime: 2021-01-20 18:52:32
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \go_demo\src\dataBaseType02\main.go
 */
package main

import "fmt"
import "unsafe"
import "strings"
import "strconv"
import "github.com/shopspring/decimal"

func getUserInfo() (string, int) {
	return "liuy", 18
}

func main() {

  /*
  string类型默认输出为空：""
  int类型默认输出为0
  float类型默认输出为0
  bool类型默认输出为FALSE
  */
	//Print为直接输出
	// fmt.Print("你好golang")
	//Println类似于输出最后加上/n 会自动换行 多输出时会自动添加空格
	// fmt.Println("你好golang")
	// Printf为格式化输出
	// fmt.Printf("你好golang\n")
  // var a = "abc"
  // fmt.Printf("%v\n", a)
  
  //类型推导方式定义变量
  // str := "你好golang"
  // fmt.Println(str)

	// var b int = 3
	// var c int = 5
	// fmt.Printf("a=%v b=%v c=%v \n", a, b, c)

	// var username = "张三"
	// fmt.Printf("类型为：%T", username)

	//短变量声明（只能在局部使用，不能全局使用）
	// d := "d"
	// fmt.Println(d)

	// _ 为匿名变量 （匿名变量不会占用命名空间，不会分配内存，所以不存在重复声明）
	var name, _ = getUserInfo()
	fmt.Println(name)

	/*
	*	常量 const 常量名 = 常量值
	 */

	// const A = "A"

	// const (
	// 	A = "A"
	// 	B = "B"
	// )

	// fmt.Println(A, B)

	//iota 等于计数器 0开始自增
	// const C = iota
	// fmt.Println(C)

	// const (
	// 	n1 = iota
	// 	n2
	// 	n3
	// 	n4
	// )
	//输出结果：0 1 2 3
	// fmt.Println(n1,n2,n3,n4)

	//iota可以在声明中插队
	// const (
	// 	n1 = iota
	// 	n2 = 100
	// 	n3 = iota
	// 	n4
	// )
	//输出：0 100 2 3
	// fmt.Println(n1,n2,n3,n4)

  //多个iota定义在一行
  /*
	const (
		n1, n2 = iota + 1, iota + 2 // 0
		n3, n4                      // 1
		n5, n6                      // 2
	)
	//输出：1 2 - 2 3 - 3 4
	fmt.Println(n1, n2)
	fmt.Println(n3, n4)
  fmt.Println(n5, n6)
  */

  //unsafe.Sizeof 查看不同长度的整型在内存里的存储空间
  //int为有符号整型，uint为无符号整型
  // var num int32 = -15
  // fmt.Printf("num=%v 类型：%T\n",num,num)
  // fmt.Println(unsafe.Sizeof(num))

  //int类型转换，同长度才允许计算
  // var n1 int16 = 10
  // var n2 int32 = 20
  // fmt.Println(int32(n1) + n2)

  //高位向低位转换的时候要注意
  // var n3 int16 = 130
  // fmt.Println(int8(n3)) //-126 有问题了

  //数字字面量语法  %d 表示10进制输出 %b表示二进制输出 %o表示八进制输出 %x表示16进制输出
  // num := 30
  // fmt.Printf("num=%v 类型：%T\n",num , num)
  // fmt.Println(unsafe.Sizeof(num)) //表示64位的计算机 int就是int64 占用8字节

  // fmt.Printf("num=%v \n",num)   //%v 表示原样输出
  // fmt.Printf("num=%b \n",num)   //%b 表示二进制输出

  //GO 语言中浮点数默认是float64
  //%f表示输出实数,以小数形式输出,默认情况下保留小数点6位。 %.2f输出数据的时候保留2位小数
  // var flo float32 = 3.1415926
  // fmt.Printf("float=%f，float=%.2f \n",flo,flo) //float=3.141593，float=3.14

  //Golang 科学计数法表示浮点类型
  // var f2 = 3.14e2 //表示f2等于3.14*10的2次方
  // fmt.Printf("%v--%T \n",f2,f2)  //314

  // var f3 = 3.14e-2    //表示f3等于3.14除以10的2次方
  // fmt.Printf("%v--%T \n",f3,f3) //0.0314

  //float精度丢失问题
  //几乎所有的变成语言都有精度丢失这个问题，这是典型的二进制浮点数精度损失问题，在定长条件下，二进制小数和十进制小数互转可能有精度丢失
  var d float64 = 1129.6
  fmt.Println((d * 100)) //112959.99999999999

  m1 := 8.2
  m2 := 3.8
  fmt.Println(m1 - m2) //期望是 4.4，结果打印出了：4.3999999999999995

  // 解决办法 ：第三方包解决  (github.com/shopspring/decimal)
  // 加法：Add  减法：Sub 乘法：Mul 除法：Div
  d1 := decimal.NewFromFloat(m1).Add(decimal.NewFromFloat(m2))
  fmt.Println(d1) //12

  /*
    Go语言中以bool类型进行声明布尔型数据,布尔型数据只有true (真)和false (假)两个值。
    
    注意:
    1·布尔类型变量的默认值为false.
    2.Go语言中不允许将整型强制转换为布尔型
    3,布尔型无法参与数值运算,也无法与其他类型进行转换。
  */

  var res bool = true;
  fmt.Printf("res=%v -- 类型：%T \n",res,res)

  /*
    string类型：
  */
  //字符串转义符：\n \' \"等等，如果输出的字符串中包含需要原样输出\\n
  var str1 string = "E:\\go_demo"
  fmt.Println(str1)

  //多行字符串  （会原样输出）
  str2 := `this is line 1
  this is line 2
  this is line 3
  this is line 4
  `
  //字符串拼接 连接符必须写在每行最后面
  str2 = "test1" +
  "test2" +
  "test3"
  fmt.Println(str2)

  /*
    
    字符串的常用操作：

    len(str)  求长度
    + 或 fmt.Sprintf 拼接字符串
    strings.Split 分割
    strings.contains  判断是否包含
    strings.HasPrefix,strings,HasSuffix 前缀/后缀判断
    strings.Index(),strings.LastIndex() 子串出现的位置
    string.Join(a[]string,sep string) join操作
  */
  var str3 = "你好"
  fmt.Println(len(str3))  //6 一个汉字占用三个字节

  str4 := "Golang"
  str5 := fmt.Sprintf("%v~%v",str3,str4)  //需要接收返回值
  fmt.Println(str5)

  str6 := "haha-hehe"
  //返回值：切片数据 （简单的理解切片就是数组 在golang中切片和数组还是有一些区别）
  arr := strings.Split(str6,"-")  
  fmt.Println(arr)

  // join操作表示把一个切片连接成一个字符串
  str7 := strings.Join(arr,"*")
  fmt.Println(str7)

  str8 := "hello world~"
  //前面代码已经定义了res，只需要重新赋值
  res = strings.Contains(str8,"hello")  //返回bool
  fmt.Println(res) 

  //查找是否前缀存在结果
  res = strings.HasPrefix(str8,"hello")
  fmt.Println(res) 
  //查找是否后缀存在结果
  res = strings.HasSuffix(str8,"hello")
  fmt.Println(res) 

  //从前至后查找子字符串出现的位置（从0开始）
  num := strings.Index(str8,"l")  //-1表示不存在
  fmt.Println(num)

  //从后至前查找子字符串出现的位置（从0开始）
  num = strings.LastIndex(str8,"l")  //-1表示不存在
  fmt.Println(num)

  /*
  byte 和 rune 类型 字符和字符串不一样
  组成每个字符串的元素叫做"字符”,可以通过遍历字符串元素获得字符。字符用单引号(')包裹起来

  Go语言的字符有以下两种:
  1, uint8类型,或者叫byte型,代表了ASCI码的一个字符。
  2. rune类型,代表一个UTF-8字符。
  当需要处理中文、日文或者其他复合字符时,则需要用到rune类型。rune类型实际是一个int32.Go使用了特殊的rune类型来处理Unicode,让基于Unicode的文本处理更为方便,也可以使用byte型进行默认字符串处理,性能和扩展性都有照顾。
  */
  //字符只能定义一个，可以用单引号，如果多个字符就是字符串了需要使用双引号
  var a = 'a'
  fmt.Printf("a=%v , 类型：%T \n",a, a)  //97 int32，默认输出的是ASCII码十进制值
  //需要原样输出  %c
  fmt.Printf("a=%c , 类型：%T \n",a, a) //a int32

  //定义一个字符串输出字符串里面的字符
  var b = "this"
  fmt.Printf("值：%v , 类型：%T \n",b[2], b[2]) //105 uint8

  //一个汉字占用3个字节（因为go使用的是utf8编码，GBK则是2字节），一个字母占用一个字节
  fmt.Println(unsafe.Sizeof(b))  //16，无法获取string类型存储数据所占的空间 ，需要获取字节使用len

  // 通过循环输出字符串里面的字符 
  str9 := "你好 golang"

  //通过byte类型循环获取  （纯字母使用）
  // for i := 0; i< len(str8); i++ {
  //     // 汉字输出会影响228(ä)189(½)160( )229(å)165(¥)189(½)32( )103(g)111(o)108(l)97(a)110(n)
  //     fmt.Printf("%v(%c)",str9[i],str9[i])
  // }

  // 通过rune类型循环获取（如果有汉字使用）
  // for _, v:= range str9 {
  //   //20320(你)22909(好)32( )103(g)111(o)108(l)97(a)110(n)103(g)
  //   fmt.Printf("%v(%c) \n",v,v)
  // }

  // 修改字符串
  //,要修改字符串,需要先将其转换成1rune或Ibyte,完成后再转换为string。无论哪种转换,都会重新分配内存,并复制字节数组。
  changeStr := []rune(str9)
  changeStr[0] = '我'
  fmt.Println(string(changeStr))


  /*
  数据之间的转换
  int32(),float32(),string()等等
  */
  //使用Sprintf也可以转换string
  //注意: Sprintf使用中需要注意转换的格式int为%d float为%f bool为%t byte为%c
  var f int64 = 12
  str10 := fmt.Sprintf("%d",f)
  fmt.Printf("值：%v 类型：%T \n",str10,str10)

  //通过strconv 把其他类型转换成string类型  
  //FormatInt(参数1：必须为int64，传值int类型的进制)
  str11 := strconv.FormatInt(f,10)
  fmt.Printf("值：%v 类型：%T \n",str11,str11)

  /*
  FormatFloat
  参数1：要转换的值
  参数2：格式化类型
  'f (-ddd.dddd)、 用的最多的，格式化成常见的格式
  'b' (-dddpddd, 指数为二进制)、
  'e' (-d.ddddetdd, t进制指数)、
  'E' (-d.ddddEtdd, +进制指数)、
  'g (指数很大时用'e格式,否则 格式)、
  'G' (指数很大时用E格式,否则 格式) 。
  参数3：保留的小数点 -1 (不对小数点格式化)
  参数4：格式化的类型
  */
  var t float64 = 20.23
  str12 := strconv.FormatFloat(t,'f',2,64)
  fmt.Printf("值：%v 类型：%T \n",str12,str12)

  str13 := strconv.FormatBool(true)
  fmt.Printf("值：%v 类型：%T \n",str13,str13)

  str14 := strconv.FormatUint(uint64('a'),10)
  fmt.Printf("值：%v 类型：%T \n",str14,str14)

  //string类型转换成数值类型
  /*
  ParseInt
  参数1：string数据
  参数2：进制
  参数3：位置 16 32 64
  */
  num1 , err := strconv.ParseInt("sdf123",10,64)  //如果字符串内不是纯数字
  fmt.Printf("num1 = %v -- %T \n", num1,num1)
  //err :strconv.ParseInt: parsing "sdf123": invalid syntax -- *strconv.NumError
  fmt.Printf("err :%v -- %T \n", err,err)

  /*
  ParseFloat
  参数1：string数据
  参数2：位数 32 64
  */
  num2, _ := strconv.ParseFloat("123.123",64)
  fmt.Printf("num2 = %v -- %T \n", num2,num2)

  //不建议把string类型转换成bool型
  b1, _ := strconv.ParseBool("test")
  fmt.Printf("b1 = %v -- %T \n", b1,b1)
  //数值类型没法和bool类型进行转换
  //注意:在go语言中数值类型没法直接转换成bool类型 bool类型也没法直接转换成数值类型

}
